package com.spzx.gateway.filter;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import lombok.Getter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import com.spzx.common.core.utils.ServletUtils;

/**
 * 黑名单过滤器，用于拦截和阻止访问黑名单中的URL路径。
 *
 * @author spzx
 */
@Component
public class BlackListUrlFilter extends AbstractGatewayFilterFactory<BlackListUrlFilter.Config>
{
    /**
     * 将配置应用于网关过滤器。根据提供的配置创建并返回一个具体的 GatewayFilter 实例。
     *
     * @param config 过滤器的配置对象，包含黑名单URL列表
     * @return 返回一个实现了特定逻辑的 GatewayFilter 实例
     */
    @Override
    public GatewayFilter apply(Config config)
    {
        return (exchange, chain) -> {

            // 获取当前请求的URL路径
            String url = exchange.getRequest().getURI().getPath();

            // 检查该URL是否匹配黑名单中的任意模式
            if (config.matchBlacklist(url))
            {
                // 如果匹配，则通过 ServletUtils 返回错误响应，告知客户端请求地址不允许访问
                return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求地址不允许访问");
            }

            // 如果不匹配黑名单，则继续处理请求链
            return chain.filter(exchange);
        };
    }

    /**
     * 构造函数，初始化父类所需的配置类型。
     */
    public BlackListUrlFilter()
    {
        super(Config.class);
    }

    /**
     * 配置类，用于存储和管理黑名单URL及其正则表达式模式。
     */
    public static class Config
    {
        /**
         * -- GETTER --
         *  获取当前的黑名单URL列表。
         *
         */
        @Getter
        private List<String> blacklistUrl;  // 存储原始的黑名单URL字符串

        private List<Pattern> blacklistUrlPattern = new ArrayList<>();  // 存储转换后的黑名单URL正则表达式模式

        /**
         * 检查给定的URL是否与任何黑名单模式匹配。
         *
         * @param url 要检查的URL路径
         * @return 如果URL匹配黑名单中的任意模式，则返回true；否则返回false
         */
        public boolean matchBlacklist(String url)
        {
            // 检查是否有黑名单URL模式，并且URL匹配任何一个模式
            return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find());
        }

        /**
         * 设置新的黑名单URL列表，并更新对应的正则表达式模式。
         *
         * @param blacklistUrl 新的黑名单URL列表
         */
        public void setBlacklistUrl(List<String> blacklistUrl)
        {
            this.blacklistUrl = blacklistUrl;

            // 清空现有的黑名单URL模式
            this.blacklistUrlPattern.clear();

            // 根据新的黑名单URL列表更新模式，将通配符 "**" 替换为正则表达式的任意字符匹配 "(.*?)"
            this.blacklistUrl.forEach(url -> {
                this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE));
            });
        }
    }

}
